import {
  canUse,
  isNull,
  PagedList,
  PageParams,
  isUndefined,
  numberToString,
  reduceToObject,
} from "mmb";
import * as brand_adminpb from "../grpc-client/service/brand_admin_pb";
import {
  getBgsBrandAdminOpSafely,
  getMetadata,
  getOptionsOfLight,
  wrapperGrpcError,
} from "../grpc-client/grpcAdapter";
import GrpcErrCode from "../grpc-client/grpcErrCode";
import { jsTimestampToPb, pbTimestampToJs } from "./helper";
import {
  BrandAdmin,
  BrandAdminWithUser,
  BecomeBrandAdminParams,
  UpdateBrandAdminParams,
  CancelBrandAdminParams,
  BrandAdminWithBrandOwnFlagByBrandIDAndUserID,
} from "./declareTypes";
import { getCache, setCache, makeEntityKey } from "../proxy/redisProxy";

export const checkBrandAdmin = async (userId: string, brandId: string) => {
  const k = makeBrandAdminCacheKey(brandId);
  const cv = await getCache(k);
  let userIdsAsBrandAdmin: string[] = [];
  if (canUse(cv)) {
    console.log("checkBrandAdmin use cached value");
    userIdsAsBrandAdmin = JSON.parse(cv!) as string[];
    console.log(`userIdsAsfor ${brandId} brandAdmin:`, userIdsAsBrandAdmin);
  } else {
    console.log("checkBrandAdmin use query");
    const brandAdmins = await listBrandAdminsByBrandIDForPermissionCheck(
      brandId
    );
    userIdsAsBrandAdmin = brandAdmins.map((o: BrandAdmin) => o.userId);
    await setCache(k, userIdsAsBrandAdmin); // replay=>OK
  }
  return userIdsAsBrandAdmin.includes(userId);
};

/****************************************************************************************/
/*                                   brand_admin entity                                 */
/****************************************************************************************/

const makeBrandAdminCacheKey = makeEntityKey("brand_admin");

const brandAdminPbToObject = (
  pb: brand_adminpb.BrandAdminEntity
): BrandAdmin => {
  return {
    brandAdminId: `${pb.getId()}`,
    userId: `${pb.getUserId()}`,
    brandId: `${pb.getBrandId()}`,
    aliasName: pb.getAliasName(),
    became: pbTimestampToJs(pb.getBecame())!,
    remark: pb.getRemark(),
  };
};

/****************************************************************************************/
/*                                   brand_admin cmd                                    */
/****************************************************************************************/

export const becomeBrandAdmin = async ({
  userId,
  brandId,
  aliasName,
  became,
}: BecomeBrandAdminParams): Promise<BrandAdmin> => {
  const cl = getBgsBrandAdminOpSafely();
  const req = new brand_adminpb.BecomeBrandAdminRequest();

  req.setUserId(Number(userId));
  req.setBrandId(Number(brandId));
  req.setAliasName(aliasName);
  req.setBecame(jsTimestampToPb(became));

  return new Promise((resolve, reject) => {
    cl.becomeBrandAdmin(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandAdminPbToObject(res.getBrandAdmin()!));
    });
  });
};

export const becomeBrandAdminCachelly = async (
  p: BecomeBrandAdminParams
): Promise<BrandAdmin> => {
  const brandAdmin = await becomeBrandAdmin(p);
  const k = makeBrandAdminCacheKey(brandAdmin.brandId);
  const cv = await getCache(k);
  let userIdsAsBrandAdmin: string[] = [];
  if (!isUndefined(cv)) {
    userIdsAsBrandAdmin = JSON.parse(cv!) as string[];
  }
  userIdsAsBrandAdmin.push(brandAdmin.userId);
  const setr = await setCache(k, userIdsAsBrandAdmin);
  console.log("becomeBrandAdminCachelly setCache:", setr);
  return brandAdmin;
};

export const updateBrandAdmin = async ({
  brandAdminId,
  aliasName,
  remark,
}: UpdateBrandAdminParams): Promise<BrandAdmin> => {
  const cl = getBgsBrandAdminOpSafely();
  const req = new brand_adminpb.UpdateBrandAdminRequest();

  req.setId(Number(brandAdminId));
  if (!isUndefined(aliasName)) {
    req.setAliasName(aliasName);
  }
  if (!isUndefined(remark)) {
    req.setRemark(remark);
  }

  return new Promise((resolve, reject) => {
    cl.updateBrandAdmin(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandAdminPbToObject(res.getBrandAdmin()!));
    });
  });
};

export const cancelBrandAdmin = async ({
  brandAdminId,
}: CancelBrandAdminParams): Promise<BrandAdmin> => {
  const cl = getBgsBrandAdminOpSafely();
  const req = new brand_adminpb.CancelBrandAdminRequest();

  req.setId(Number(brandAdminId));

  return new Promise((resolve, reject) => {
    cl.cancelBrandAdmin(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandAdminPbToObject(res.getBrandAdmin()!));
    });
  });
};

export const cancelBrandAdminCachelly = async (
  p: CancelBrandAdminParams
): Promise<BrandAdmin> => {
  const brandAdmin = await cancelBrandAdmin(p);
  const k = makeBrandAdminCacheKey(brandAdmin.brandId);
  const cv = await getCache(k);
  if (isUndefined(cv)) {
    return brandAdmin;
  }
  const userIdsAsBrandAdmin = JSON.parse(cv!) as string[];
  const idx = userIdsAsBrandAdmin.findIndex((v) => v === brandAdmin.userId);
  if (idx !== -1) {
    userIdsAsBrandAdmin.splice(idx, 1);
  }
  const setr = await setCache(k, userIdsAsBrandAdmin);
  console.log("cancelBrandAdminCachelly setCache:", setr);
  return brandAdmin;
};

export const identifiyBrandAdminEmployee = async (
  brandAdminId: string
): Promise<BrandAdmin> => {
  const cl = getBgsBrandAdminOpSafely();
  const req = new brand_adminpb.IdentifiyBrandAdminEmployeeRequest();

  req.setId(Number(brandAdminId));

  return new Promise((resolve, reject) => {
    cl.identifiyBrandAdminEmployee(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(brandAdminPbToObject(res.getBrandAdmin()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_admin query                                  */
/****************************************************************************************/

export const listBrandAdminsByBrandIDForPermissionCheck = async (
  brandId: string
): Promise<BrandAdmin[]> => {
  const cl = getBgsBrandAdminOpSafely();
  const req = new brand_adminpb.ListBrandAdminsByBrandIDRequest();

  req.setLimit(10000);
  req.setOffset(0);
  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.listBrandAdminsByBrandID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }

        const items = res
          .getBrandAdminsList()
          .map((it) => brandAdminPbToObject(it));
        resolve(items);
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_admin joins entity                           */
/****************************************************************************************/

const brandAdminWithBrandOwnFlagByBrandIDAndUserIDPbToObject = (
  pb: brand_adminpb.BrandAdminWithBrandOwnFlagByBrandIDAndUserIDEntity
): BrandAdminWithBrandOwnFlagByBrandIDAndUserID => {
  return {
    // brand_admin
    brandAdminId: `${pb.getId()}`,
    userId: `${pb.getUserId()}`,
    brandId: `${pb.getBrandId()}`,
    aliasName: pb.getAliasName(),
    became: pbTimestampToJs(pb.getBecame())!,
    remark: pb.getRemark(),
    // additional
    isBrandOwner: pb.getIsBrandOwner(),
  };
};

const brandAdminWithUserPbToObject = (
  pb: brand_adminpb.BrandAdminWithUserEntity
): BrandAdminWithUser => {
  return {
    brandAdminId: `${pb.getId()}`,
    userId: `${pb.getUserId()}`,
    brandId: `${pb.getBrandId()}`,
    aliasName: pb.getAliasName(),
    became: pbTimestampToJs(pb.getBecame())!,
    remark: pb.getRemark(),

    // user
    userNickName: pb.getUserNickName(),
    userPic: pb.getUserPic(),
  };
};

/****************************************************************************************/
/*                                   brand_admin joins query                            */
/****************************************************************************************/

export const listBrandAdminWithBrandOwnFlagsByBrandIDAndUserID = async (
  brandId: string,
  userId: string,
  pager: PageParams
): Promise<PagedList<BrandAdminWithBrandOwnFlagByBrandIDAndUserID>> => {
  const cl = getBgsBrandAdminOpSafely();
  const req =
    new brand_adminpb.ListBrandAdminWithBrandOwnFlagsByBrandIDAndUserIDRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setBrandId(Number(brandId));
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.listBrandAdminWithBrandOwnFlagsByBrandIDAndUserID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<BrandAdminWithBrandOwnFlagByBrandIDAndUserID> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getBrandAdminWithBrandOwnFlagsList()
          .map((it) =>
            brandAdminWithBrandOwnFlagByBrandIDAndUserIDPbToObject(it)
          );
        resolve(ret);
      }
    );
  });
};

export const listBrandAdminWithUsersWithoutBrandOwnerByBrandIDAndUserID =
  async (
    brandId: string,
    userId: string,
    pager: PageParams
  ): Promise<PagedList<BrandAdmin>> => {
    const cl = getBgsBrandAdminOpSafely();
    const req =
      new brand_adminpb.ListBrandAdminWithUsersWithoutBrandOwnerByBrandIDAndUserIDRequest();

    req.setLimit(pager.limit);
    req.setOffset(pager.offset);
    req.setBrandId(Number(brandId));
    req.setUserId(Number(userId));

    return new Promise((resolve, reject) => {
      cl.listBrandAdminWithUsersWithoutBrandOwnerByBrandIDAndUserID(
        req,
        getMetadata(),
        getOptionsOfLight(),
        (err, res) => {
          if (!isNull(err)) {
            reject(wrapperGrpcError(err.details));
            return;
          }
          const opRet = res.getOpRet()!;
          if (!opRet.getSuccess()) {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
          const ret: PagedList<BrandAdmin> = {
            total: 0,
            items: [],
          };
          ret.total = res.getTotal();
          ret.items = res
            .getBrandAdminWithUsersList()
            .map((it) => brandAdminWithUserPbToObject(it));
          resolve(ret);
        }
      );
    });
  };
